package algoGenetic;

import java.util.*;

/**
 * Cette classe fournit un ensemble de méthodes statiques permettant d'agir
 * sur des listes d'instances de Genome.
 * @author simon
 * @version 1.0
 */
public class Population {
	
	/**
	 * Retourne les n meilleurs individus de la liste fournie en paramètre.
	 * La sélection s'effectue selon le critère de comparaison fourni par
	 * la méthode compareTo des individus.
	 * @param individus le pool d'individus dont sera issu la sélection.
	 * L'ordre de la liste peut-être altéré à l'issu de l'appel, mais son
	 * contenu est identique.
	 * @param n le nombre d'inidvidus passant la sélection
	 * @return les n meilleurs individus de la liste fournie en paramètre
	 */
	public static List<Genome> selection(List<Genome> individus, int n){
		int s=individus.size();
		Collections.sort(individus);
		return individus.subList(s-n,s);
	}
	
	/**
	 * Retourne la liste augmentée de n individus créés par croisements.
	 * Un même individu, voire un même couple, peut générer plusieurs "enfants",
	 * la sélection des couples se faisant de manière itérative et aléatoire.
	 * La génération de nouveaux individus se fait selon la méthode croisement
	 * fournie par les génomes.
	 * @param individus le pool d'individus qui génèreront les nouveaux individus
	 * @param n le nombre de nouvelles solutions engendrées
	 * @return la liste augmentée de n individus créés par croisements
	 */
	public static List<Genome> reproduction(List<Genome> individus, int n){
		int s=individus.size(); //taille de la liste d'origine
		Random r=new Random();
		int a,b;
		for (int i=0;i<n;i++){
			a=r.nextInt(s);
			Genome genA=individus.get(a);
			do {
				b=r.nextInt(s);
			} while (a==b); //les deux individus sélectionnées doivent être distincts
			Genome genB=individus.get(b);
			Genome genC=genA.croisement(genB);
			individus.add(genC);
		}
		return individus;
		
	}
	
	/**
	 * Mute tous les individus de la liste fournie en paramètre
	 * La mutation est celle décrite par la méthode mutation des individus
	 * @param individus la liste à muter
	 * @return
	 */
	public static List<Genome> mutation(List<Genome> individus){
		for(Genome i: individus){
			i.mutation();
		}
		return individus;
	}
	
	/**
	 * Engendre une liste de n individus par sélection, croisement puis mutation
	 * de la liste fournie en paramètre.
	 * @param individus
	 * @param n la taille de la liste retournée
	 * @param m le nombre d'individus initialement sélectionnés. Un trop petit
	 * nombre génèrera une liste d'individus proches les uns des autres, tandis
	 * qu'un nombre trop proche de <code>individus.size()</code> n'assurera pas
	 * un génome de qualité
	 * @return une liste de n individus engendrés par sélection, croisement,
	 * puis mutation de la liste fournie en paramètre 
	 */
	public static List<Genome> generation(List<Genome> individus, int n, int m){
		individus=selection(individus,m);
		individus=reproduction(individus,n-m);
		individus=mutation(individus.subList(m+1, n)); //seuls les enfants sont mutés
		return individus;
	}

}
